#include "DIVA_Manager.h"
#include "DIVA_VM.h"
#include "lock.h"
#include "ExplorBotLibrary.h"
#include "LayoutLibrary.h"

DIVA_Manager* DIVA_Manager::DM = NULL;

/// constructor
DIVA_Manager::DIVA_Manager(Ogre::SceneManager* SMgr){
	_SceneMgr = SMgr;
	_Camera = SMgr->getCamera("Diva_Camera");
	mRaySceneQuery = SMgr->createRayQuery(Ogre::Ray());
	_parentnode = NULL ;
	_animationonoff = true ;
}

/// destructor
DIVA_Manager::~DIVA_Manager(){
}

/// singleton method 
DIVA_Manager* DIVA_Manager::getInstance(){
	assert(DM);
	return DM;
}

/// initial method
void DIVA_Manager::init(Ogre::SceneManager* SMgr){
	assert(!DM);
	if(!DM)
		DM = new DIVA_Manager(SMgr);
}

/// add the vm to the vm list
bool DIVA_Manager::add(DIVA_VM* dvm){
	all_VM_List.push_back(dvm);
	return true;
}

///
/*
bool DIVA_Manager::remove(DIVA_VM* dvm){
	all_VM_List.erase(dvm);
	return true;
}
*/

/// get all vm list
std::vector<DIVA_VM* > DIVA_Manager::get_all_VM_List(){
	return all_VM_List;
}

/// update all vm list
bool DIVA_Manager::update_all_VM_List(){
	std::vector<DIVA_VM* >::iterator itr;
	/*
	for (itr = all_VM_List.begin(); itr != all_VM_List.end(); itr++){
		//(*itr)->animate();
	} // end for
    */
	return true;
}

/// 
std::map<Ogre::MovableObject*, DIVA_VM* >* DIVA_Manager::get_vm_MovableObject_Table(){
	return &vm_MovableObject_Table;
}

/// 傳入entity 作table search  
DIVA_VM* DIVA_Manager::get_VM_from_MovableObject(Ogre::MovableObject* _ent){
	
	std::map<Ogre::MovableObject*, DIVA_VM*> *_table = get_vm_MovableObject_Table();
	std::map<Ogre::MovableObject*, DIVA_VM*>::iterator itr = _table->begin();

	//return vm_MovableObject_Table[_ent];
	
	for (; itr != _table->end(); itr++){
		if (itr->first == _ent){
			return itr->second;
		}
	}
	return NULL;
	
}

/// 傳入vm 做table search
Ogre::MovableObject* DIVA_Manager::get_MovableObject_from_VM(DIVA_VM* _dvm){

	std::map<Ogre::MovableObject*,DIVA_VM* > *_table = get_vm_MovableObject_Table();
	std::map<Ogre::MovableObject*,DIVA_VM* >::iterator itr = _table->begin();

	//return vm_MovableObject_Table[_dvm];
	
	for(;itr != _table->end(); itr++){
		if ((itr->second) == _dvm) {
			return itr->first;
		}
	}
	return NULL;
	

}

/// 取得scene manager
Ogre::SceneManager* DIVA_Manager::getSceneManager(){
	return _SceneMgr;
}

/// 作ray scene query
void DIVA_Manager::ray_scene_query(Ogre::Real x, Ogre::Real y){
	hitting_objs.clear();
	// Setup 

	Ogre::Ray mouseRay = _Camera->getCameraToViewportRay(x,y); // the ray scene query
	mRaySceneQuery->setRay(mouseRay);
	mRaySceneQuery->setSortByDistance( true );
				
	// Execute query
	Ogre::RaySceneQueryResult &result = mRaySceneQuery->execute();	
	Ogre::RaySceneQueryResult::iterator itr;
	for (itr= result.begin(); itr != result.end(); itr++) {
		if (itr->movable) {
			if (itr->movable->getName().find("MovableText") == std::string::npos &&
				!itr->movable->getName().compare(0,7,"DIVA_VM"))
				hitting_objs.push_back(itr->movable);
		}
	}
}

/// pop the topest vm by ray scene query
DIVA_VM* DIVA_Manager::pop_front_hitting_VM(){
	DIVA_VM* temp = NULL;
	if(hitting_objs.size()>0){
		Ogre::MovableObject* obj = hitting_objs.front();	
		hitting_objs.erase(hitting_objs.begin());
		temp = get_VM_from_MovableObject(obj);
	}
	return temp;
}

/// reg_MovableObj_VM_relation - register the relation between the movable object and diva vm
void DIVA_Manager::reg_MovableObj_VM_relation(Ogre::MovableObject* e,DIVA_VM* vm){
    std::map<Ogre::MovableObject*, DIVA_VM* > *_table = get_vm_MovableObject_Table();
	_table->insert(std::make_pair(e,vm));

	//vm_MovableObject_Table[vm] = e;  <-- 這種寫法會有很大的問題，map的話就用insert的方式才是最保險的( modify by mike)
}
void DIVA_Manager::dereg_MovableObj_VM_relation(Ogre::MovableObject* e,DIVA_VM* vm){
    std::map<Ogre::MovableObject*, DIVA_VM* > *_table = get_vm_MovableObject_Table();
	std::map<Ogre::MovableObject*, DIVA_VM* >::iterator itr ;
	itr = _table->find(e);
	if (itr != _table->end()) {
		_table->erase(itr);
	} else
		assert(false); // why you want to deregister a (vm,e) pair which does not exist

	//vm_MovableObject_Table[vm] = e;  <-- 這種寫法會有很大的問題，map的話就用insert的方式才是最保險的( modify by mike)
}
void DIVA_Manager::lock() {
    _lock = new Lock(_animation_mutex);
}
void DIVA_Manager::release() {
	delete _lock ;
}

void DIVA_Manager::addVMAnimationListener(DIVA_VM *vm) { 
	_temp_anim_add.push_back(vm);
	//_animation_list.push_back(vm);
}

/** In WOP_Ref_Entry.cpp, A dummy VM is created first, then passed as a parameter for the creation of a VM.
    However, in a VM's constructor, a VM may call addVMAnimationListern to begin accept events of 
	VMAnimation(). It seems Ogre works on a multithreaded manner. So, that when the registration finished
	the VMAnimation begins to be invoked in frameStarted().
	So, we introduce this guard to prevent such thing from happening
*/
 void DIVA_Manager::setAnimationOnOff(bool b) { _animationonoff = b ; }

void DIVA_Manager::removeVMAnimationListener(DIVA_VM *vm) {

	_temp_anim_remove.push_back(vm);
	/*
	std::vector<DIVA_VM *>::iterator pos ;
	pos = find (_animation_list.begin(), _animation_list.end(), vm) ;
	assert(pos != _animation_list.end()) ;
	_animation_list.erase(pos);
*/
}
void DIVA_Manager::triggerAllVMAnimation(const Ogre::FrameEvent& evt) {
		
	// Lock* lock= new Lock(_animation_mutex);
	std::vector<DIVA_VM *>::iterator pos ;
		
	for (pos= _animation_list.begin(); pos != _animation_list.end();pos++) {
		if ((*pos)->isVMAnimationOn()){
			(*pos)->VMAnimation(evt);
		}
	}

	for(pos = _temp_anim_add.begin(); pos != _temp_anim_add.end();pos++){
		_animation_list.push_back((*pos));
	}
	
	for(pos = _temp_anim_remove.begin(); pos != _temp_anim_remove.end();pos++){
		std::vector<DIVA_VM *>::iterator it ;
		it = find (_animation_list.begin(), _animation_list.end(), (*pos)) ;
		assert(it != _animation_list.end()) ;
		_animation_list.erase(it);
	}

	_temp_anim_add.clear();
	_temp_anim_remove.clear();
	// delete lock ;
}

	